package com.reconcile.phone.common.utils;


import static com.reconcile.phone.common.config.WxAppLoginProperties.WX_APP_OPEN_APP_ID;
import static com.reconcile.phone.common.config.WxAppLoginProperties.WX_APP_OPEN_APP_SECRET;
import static com.reconcile.phone.common.config.XcxLoginProperties.TRY_COUNT;
import static com.reconcile.phone.common.config.XcxLoginProperties.XCX_OPEN_APP_ID;
import static com.reconcile.phone.common.config.XcxLoginProperties.XCX_OPEN_APP_SECRET;
import static com.reconcile.phone.common.constant.LoginConstant.REDIS_KEY_XCX_ACCESS_TOKEN;
import static com.reconcile.phone.common.constant.LoginConstant.WX_APP_LOGIN_URL;
import static com.reconcile.phone.common.constant.LoginConstant.XCX_GET_ACCESS_TOKEN;
import static com.reconcile.phone.common.constant.LoginConstant.XCX_GET_PHONE_NUMBER;
import static com.reconcile.phone.common.constant.LoginConstant.XCX_LOGIN_URL;

import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.reconcile.phone.common.core.domain.wechat.WxAppLoginResp;
import com.reconcile.phone.common.core.domain.wechat.XcxGetPhoneResp;
import com.reconcile.phone.common.core.domain.wechat.XcxLoginResp;
import com.reconcile.phone.common.exception.ServiceException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import javax.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

/**
 * 微信相关工具
 *
 * @author zlj
 * @since 2023/3/8
 */
@Component
@Slf4j
public class WxUtil {

  @Autowired
  private StringRedisTemplate redisTemplate;

  private static StringRedisTemplate staticRedisTemplate;
  /**
   * access_token获取计数器
   */
  private static Integer count = 0;

  @PostConstruct
  public void init() {
    staticRedisTemplate = redisTemplate;
  }

  /**
   * 小程序登陆
   *
   * @param code 登陆code
   * @return 小程序响应信息
   */
  public static XcxLoginResp xcxLogin(String code) {
    Map<String, Object> param = new HashMap<>();
    param.put("appid", XCX_OPEN_APP_ID);
    param.put("secret", XCX_OPEN_APP_SECRET);
    param.put("grant_type", "authorization_code");
    param.put("js_code", code);
    String response = HttpUtil.get(XCX_LOGIN_URL, param);
    return JSON.parseObject(response, XcxLoginResp.class);
  }

  /**
   * 小程序获取 access_token
   *
   * @return access_token
   */
  public static String xcxGetAccessToken() {
    //从缓存获取accessToken
    String accessToken = staticRedisTemplate.opsForValue().get(REDIS_KEY_XCX_ACCESS_TOKEN);
    if (StringUtils.isNotEmpty(accessToken)) {
      return accessToken;
    }
    //缓存不存在, 重新申请
    Map<String, Object> param = new HashMap<>();
    param.put("appid", XCX_OPEN_APP_ID);
    param.put("secret", XCX_OPEN_APP_SECRET);
    param.put("grant_type", "client_credential");
    String response = HttpUtil.get(XCX_GET_ACCESS_TOKEN, param);
    //解析
    JSONObject jsonObject = JSON.parseObject(response);
    accessToken = jsonObject.getString("access_token");
    if (StringUtils.isBlank(accessToken)) {
      count++;
      if (count > TRY_COUNT) {
        throw new RuntimeException("获取access_token失败");
      }
      log.info("第" + count + "次尝试获取access_token");
      //获取失败重试
      xcxGetAccessToken();
    }
    count = 0;
    Integer expiresIn = jsonObject.getInteger("expires_in");
    staticRedisTemplate.opsForValue().set(REDIS_KEY_XCX_ACCESS_TOKEN, accessToken, expiresIn - 300, TimeUnit.SECONDS);
    return accessToken;
  }

  /**
   * 小程序获取用户手机号
   *
   * @param code 微信授权code
   * @return 手机号
   */
  public static String xcxGetPhoneNumber(String code) {
    Map<String, String> map = new HashMap<>();
    map.put("code", code);
    String response = null;
    try {
      response = HttpUtil.post(XCX_GET_PHONE_NUMBER + "?access_token=" + xcxGetAccessToken(), JSON.toJSONString(map));
      XcxGetPhoneResp phoneResp = JSON.parseObject(response, XcxGetPhoneResp.class);
      XcxGetPhoneResp.PhoneInfoBean phoneInfo = phoneResp.getPhoneInfo();
      return phoneInfo.getPhoneNumber();
    } catch (Exception e) {
      Object errcode = JSON.parseObject(response).get("errcode");
      log.info("获取手机号失败: " + errcode);
      throw new ServiceException("获取手机号失败");
    }
  }

  /**
   * app登陆
   *
   * @param code 登陆code
   * @return 小程序响应信息
   */
  public static WxAppLoginResp appLogin(String code) {
    Map<String, Object> param = new HashMap<>();
    param.put("appid", WX_APP_OPEN_APP_ID);
    param.put("secret", WX_APP_OPEN_APP_SECRET);
    param.put("grant_type", "authorization_code");
    param.put("code", code);
    String response = HttpUtil.get(WX_APP_LOGIN_URL, param);
    return JSON.parseObject(response, WxAppLoginResp.class);
  }
}
